package cn.xiaolang.function.sql;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 动态SQL拼接处理工具类，参数部分使用?|$|#代替。
 * <ul>
 * $代表常量(替换时不加单引号)
 * </ul>
 * <ul>
 * #代表变量(替换时要加单引号)
 * </ul>
 *
 * @author fuhw/Dean
 */
public class DynamicSQLHelper {

    private final StringBuilder sql = new StringBuilder();

    private final ArrayList<Object> params = new ArrayList<>();

    /**
     * 参数不为NULL、""的时候，拼接sqlTip
     *
     * @param sqlTip   sql tip
     * @param paramVal the value of parameter
     * @return if paramVal != null  then append sqlTip.
     */
    public <T> DynamicSQLHelper isNotEmpty(String sqlTip, T paramVal) {
        if (paramVal == null || "".equals(paramVal)) {
            return this;
        }
        this.sql.append(sqlTip);
        this.params.add(paramVal);
        return this;
    }

    /**
     * 参数为NULL、""的时候，拼接sqlTip
     *
     * @param sqlTip sql tip
     * @param param  sql parameters
     * @return T
     */
    public <T> DynamicSQLHelper isEmpty(String sqlTip, T param) {
        if (param == null || "".equals(param)) {
            this.sql.append(sqlTip);
            this.params.add(param);
        }
        return this;
    }

    /**
     * paramVal==cmpVal || paramVal equals cmpVal 的时候拼接
     *
     * @param paramVal the value of parameter
     * @param cmpVal   compared value
     * @return if paramVal != cmpVal then append sqlTip.
     */
    public <T> DynamicSQLHelper isNotEqual(String sqlTip, T paramVal, T cmpVal) {
        if (this.isBaseType(paramVal) && paramVal == cmpVal) {
            return this;
        }
        if (!this.isBaseType(paramVal) && paramVal.equals(cmpVal)) {
            return this;
        }
        this.sql.append(sqlTip);
        this.params.add(paramVal);
        return this;
    }

    /**
     * paramVal!=cmpVal || paramVal !equals cmpVal 的时候拼接
     *
     * @param sqlTip   sql tip
     * @param paramVal the value of parameter
     * @param cmpVal   compared value
     * @return if paramVal == cmpVal then append sqlTip.
     */
    public <T> DynamicSQLHelper isEqual(String sqlTip, T paramVal, T cmpVal) {

        if (this.isBaseType(paramVal) && paramVal != cmpVal) {
            return this;
        }

        if (!this.isBaseType(paramVal) && !paramVal.equals(cmpVal)) {
            return this;
        }
        this.sql.append(sqlTip);
        this.params.add(paramVal);
        return this;
    }

    /**
     * paramVal!=null 时候拼接
     *
     * @param sqlTip   sql tip
     * @param paramVal the value of parameter
     * @return if paramVal != null then append sqlTip.
     */
    public <T> DynamicSQLHelper isNotNull(String sqlTip, T paramVal) {
        if (paramVal == null) {
            return this;
        }
        this.sql.append(sqlTip);
        this.params.add(paramVal);
        return this;
    }

    /**
     * paramVal==null 时候拼接
     *
     * @param sqlTip   sql tip
     * @param paramVal the value of parameter
     * @return if paramVal == null then append sqlTip.
     */
    public <T> DynamicSQLHelper isNull(String sqlTip, T paramVal) {
        if (paramVal == null) {
            this.sql.append(sqlTip);
        }
        return this;
    }

    /**
     * 正常拼接
     *
     * @param sqlTip sql tip
     * @return DynamicSQLHelper
     */
    public DynamicSQLHelper append(String sqlTip) {
        this.sql.append(sqlTip);
        return this;
    }

    /**
     * 获取动态拼接后的sql，参数?模式。
     *
     * @return sql String
     */
    public String getSql() {
        String _sql = trimOf();
        return _sql.replaceAll("\\$", "?").replaceAll("#", "?");
    }

    /**
     * 获取参数的集合，有顺序与getSql()获取到的sql中的?参数一一对应。
     *
     * @return List<Object> of parameters
     */
    public List<Object> getParams() {
        this.params.trimToSize();
        return this.params;
    }

    /**
     * 获取参数的集合，有顺序与getSql()获取到的sql中的?参数一一对应。
     *
     * @param parameter DynamicSQLHelper.Parameter
     * @return T
     */
    public <T> T getParams(DynamicSQLHelper.Parameter<T> parameter) {
        this.params.trimToSize();
        return parameter.addAll(params);
    }

    /**
     * 返回参数赋值之后完整的sql
     */
    @Override
    public String toString() {
        Object[] parameters = new String[this.params.size()];
        for (int i = 0; i < parameters.length; i++) {
            if (this.params.get(i) == null) {
                parameters[i] = null;
            } else {
                parameters[i] = String.valueOf(this.params.get(i));
            }
        }
        String _sql = trimOf();
        _sql = _sql.replaceAll("#", "'%s'").replaceAll("\\$", " %s ");

        return String.format(_sql, parameters);
    }


    private boolean isBaseType(Object param) {
        return param instanceof Byte || param instanceof Short || param instanceof Integer || param instanceof Long
                || param instanceof Float || param instanceof Double || param instanceof Boolean
                || param instanceof Character || param == null;
    }

    private String trimOf() {
        String _sql = this.sql.toString().replaceAll("\t", " ");
        while (_sql.contains("  ")) {
            _sql = _sql.replaceAll(" {2}", " ");
        }
        return _sql;
    }

    public interface Parameter<T> {
        T addAll(ArrayList<Object> params);
    }

    public void clear() {
        this.sql.setLength(0);
        this.params.clear();
    }
}